﻿/*
Copyright (c) 2009 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using TeachMe.Application.Configuration;
using TeachMe.Application.Data;
using TeachMe.Application.Properties;
using TeachMe.Application.Translators;

namespace TeachMe.Application.Forms
{
    public partial class AdvancedEntryEditorForm : PseudoModalForm
    {
        private class TranslateThreadWorkerData : ThreadWorkerData
        {
            public string[] Result { get; set; }
            public string Word { get; set; }
            public string From { get; set; }
            public string To { get; set; }
            public ITranslationProvider TranslationProvider { get; set; }
        }

        private class TranslatedItem
        {
            public string Text { get; set; }

            public override string ToString()
            {
                return Text;
            }
        }

        private readonly List<EditedEntryDescriptor> _newEntries = new List<EditedEntryDescriptor>();
        private readonly List<EditedEntryDescriptor> _entries;
        private readonly Root _root;
        private bool _ignoreTranslationsCheckStateChanged;
        private int _currentItemIdx;

        public AdvancedEntryEditorForm(Root root, List<EditedEntryDescriptor> entries, Dictionary defaultSelection)
        {
            _root = root;
            _entries = entries;

            Dictionary = defaultSelection;
            
            _root.MachineSettings.CheckOut();
            _root.UserSettings.CheckOut();

            InitializeComponent();
            InitializeDictionaries(root, defaultSelection);

            cbPriority.FillPriorities();

            cbTranslateAutomatically.Checked = _root.UserSettings.TranslateAutomatically.Value;

            WindowProportions.Lock(this, _root.MachineSettings);
        }

        private void InitializeDictionaries(Root root, Dictionary defaultSelection)
        {
            if (defaultSelection != null)
            {
                Dictionary = defaultSelection;

                ComboBoxItem<Dictionary> comboBoxItem = new ComboBoxItem<Dictionary>(Dictionary, Dictionary.Entries.Name);

                cbDictionary.Items.Add(comboBoxItem);
                cbDictionary.SelectedItem = comboBoxItem;
                cbDictionary.Enabled = false;
            }
            else
            {
                string lastEditedDictionary = _root.MachineSettings.LastEditedDictionaryPath;

                foreach (Dictionary dictionary in root.Dictionaries)
                {
                    ComboBoxItem<Dictionary> item = new ComboBoxItem<Dictionary>(dictionary, dictionary.Entries.Name);

                    cbDictionary.Items.Add(item);

                    if (String.Compare(dictionary.UserData.Path, lastEditedDictionary, true) == 0)
                    {
                        cbDictionary.SelectedItem = item;
                    }
                }
            }
        }

        private void UpdateView()
        {
            EditedEntryDescriptor descriptor = _entries[_currentItemIdx];

            tbFrom.Text = descriptor.From;
            tbTo.Text = descriptor.To;
            tbExample.Text = descriptor.Example;
            cbPriority.Priority = descriptor.Priority;

            boxItems.Items.Clear();
            
            if (cbTranslateAutomatically.Checked && CanTranslate())
            {
                Translate();
            }
            else
            {
                UpdateTranslationsToolbar();
            }

            UpdateMainToolbar();
        }

        private bool CanTranslate()
        {
            ITranslationProvider[] translators = GetAvailableTranslators();
            return translators != null && translators.Length > 0;
        }

        private ITranslationProvider[] GetAvailableTranslators()
        {
            if (Dictionary == null)
            {
                return null;
            }

            string from = Dictionary.Entries.From;
            string to = Dictionary.Entries.To;

            if (String.IsNullOrEmpty(from) || String.IsNullOrEmpty(to))
            {
                return null;
            }

            Languages.Item fromLanguageItem = Languages.GetByCode(from);
            Languages.Item toLanguageItem = Languages.GetByCode(to);

            if (fromLanguageItem == null || toLanguageItem == null)
            {
                return null;
            }

            return Translators.Translators.GetAvailableTranslators(fromLanguageItem, toLanguageItem);
        }

        private void UpdateMainToolbar()
        {
            tsbFirst.Enabled = _currentItemIdx != 0;
            tsbLast.Enabled = _currentItemIdx != _entries.Count - 1;
            tsbPrevious.Enabled = _currentItemIdx > 0;
            tsbNext.Enabled = _currentItemIdx < _entries.Count - 1;
            tstProgress.Text = String.Format("({0}/{1})", _currentItemIdx + 1, _entries.Count);
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            if (DialogResult == DialogResult.OK && Dictionary != null)
            {
                _root.MachineSettings.LastEditedDictionaryPath = Dictionary.UserData.Path;
                _root.UserSettings.TranslateAutomatically.Value = cbTranslateAutomatically.Checked;
            }

            _root.UserSettings.CheckIn();
            _root.MachineSettings.CheckIn();
        }

        private void UpdateCurrent()
        {
            EditedEntryDescriptor current = _entries[_currentItemIdx];

            current.To = tbTo.Text;
            current.From = tbFrom.Text;
            current.Example = tbExample.Text;
            current.Priority = cbPriority.Priority;
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (DialogResult == DialogResult.OK)
            {
                UpdateCurrent();
            }
        }

        private void OnTranslateClick(object sender, EventArgs e)
        {
            Translate();
        }

        private void Translate()
        {
            string inputText = NormalizeFromText(tbFrom.Text);

            if (String.IsNullOrEmpty(inputText))
            {
                return;
            }

            TranslateThreadWorkerData worker = new TranslateThreadWorkerData { Word = inputText, From = Dictionary.Entries.From, To = Dictionary.Entries.To, TranslationProvider = (ITranslationProvider)tcbTranslators.SelectedItem };
            
            BusyForm form = new BusyForm(worker);
            _root.FormsManager.Add(form);
            form.PseudoParent = this;
            form.WaitCaption = Resources.Translating;
            form.Show();

            form.Closed += delegate
                               {
                                   if (!worker.Canceled)
                                   {
                                       AddTranslations(worker.Result);
                                   }

                                   UpdateTranslationsToolbar();

                                   _root.FormsManager.Remove(form);
                               };

            Thread thread = new Thread(TranslateThreadWorker);
            thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
            thread.Start(worker);
        }

        private static string NormalizeFromText(string from)
        {
            // removing pronunciation
            // http://code.google.com/p/teach-me/issues/detail?id=44
            
            if (!String.IsNullOrEmpty(from))
            {
                from = Regex.Replace(from, "\\[.*\\]", String.Empty);
            }

            if (!String.IsNullOrEmpty(from))
            {
                from = from.Trim();
            }

            return from;
        }

        private void AddTranslations(IEnumerable<string> texts)
        {
            boxItems.Items.Clear();

            foreach (string text in texts)
            {
                boxItems.Items.Add(new TranslatedItem { Text = text }, false);
            }

            UpdateTranslatedTextsCheckState();
        }

        private List<string> ExtractToValues()
        {
            string[] parts = Regex.Split(tbTo.Text, ",\\s*(?=[^)]*(?:\\(|$))");
            List<string> result = new List<string>();

            foreach (string part in parts)
            {
                string trimmed = part.Trim();
                if (!String.IsNullOrEmpty(trimmed))
                {
                    result.Add(trimmed);
                }
            }

            return result;
        }

        private static void TranslateThreadWorker(object obj)
        {
            TranslateThreadWorkerData worker = obj as TranslateThreadWorkerData;
            if (worker == null)
            {
                throw new InvalidOperationException();
            }

            worker.UpdateStatus(String.Format(Resources.TranslatingWordStatus, worker.Word));
            worker.Result = worker.TranslationProvider.Translate(worker.Word, worker.From, worker.To);
            worker.Exit();
        }

        private void OnSelectAllClick(object sender, EventArgs e)
        {
            for (int idx = 0; idx < boxItems.Items.Count; idx++)
            {
                boxItems.SetItemChecked(idx, true);
            }
        }

        private void OnDeselectAllClicked(object sender, EventArgs e)
        {
            for (int idx = 0; idx < boxItems.Items.Count; idx++)
            {
                boxItems.SetItemChecked(idx, false);
            }
        }

        private void OnFormShown(object sender, EventArgs e)
        {
            UpdateView();
            UpdateButtons();
            UpdateTranslationsPanel();
            UpdateTranslationsToolbar();
            UpdateTexts();
        }

        private void UpdateTranslatedTextsCheckState()
        {
            List<string> toValues = ExtractToValues();

            for (int idx = 0; idx < boxItems.Items.Count; idx++)
            {
                TranslatedItem ti = boxItems.Items[idx] as TranslatedItem;
                if (ti == null)
                {
                    throw new InvalidOperationException();
                }

                bool check = false;

                foreach (string toValue in toValues)
                {
                    if (String.Compare(toValue, ti.Text, true) == 0)
                    {
                        check = true;
                    }
                }

                boxItems.SetItemChecked(idx, check);
            }
        }

        private void OnTranslatedItemChecked(object sender, ItemCheckEventArgs e)
        {
            if (_ignoreTranslationsCheckStateChanged)
            {
                return;
            }

            TranslatedItem ti = boxItems.Items[e.Index] as TranslatedItem;
            if (ti == null)
            {
                throw new InvalidOperationException();
            }

            List<string> toValues = ExtractToValues();

            StringBuilder sb = new StringBuilder();
            bool found = false;

            foreach (string toValue in toValues)
            {
                if (String.Compare(toValue, ti.Text, true) == 0)
                {
                    found = true;

                    if (e.NewValue == CheckState.Unchecked)
                    {
                        continue;
                    }
                }

                sb.Append(toValue);
                sb.Append(", ");
            }

            if (!found && e.NewValue != CheckState.Unchecked)
            {
                sb.Append(ti.Text);
            }

            tbTo.Text = sb.ToString().Trim(',', ' ');
        }

        private void OnSelectedDictionaryChanged(object sender, EventArgs e)
        {
            ComboBoxItem<Dictionary> comboBoxItem = cbDictionary.SelectedItem as ComboBoxItem<Dictionary>;

            if (comboBoxItem != null)
            {
                Dictionary = comboBoxItem.Item;
            }
            
            UpdateButtons();
            UpdateTranslationsToolbar();
            UpdateTranslationsPanel();
            UpdateTexts();
        }

        private void UpdateTexts()
        {
            if (Dictionary == null)
            {
                txFrom.Text = Resources.EntryEditorFromNoLang;
                txTo.Text = Resources.EntryEditorToNoLang;

                return;
            }

            string from = Dictionary.Entries.From;
            string to = Dictionary.Entries.To;

            string fromLanguage = Languages.GetByCode(from) != null ? Languages.GetByCode(from).Localized : from;
            string toLanguage = Languages.GetByCode(to) != null ? Languages.GetByCode(to).Localized : to;

            txFrom.Text = String.Format(Resources.EntryEditorFrom, fromLanguage);
            txTo.Text = String.Format(Resources.EntryEditorTo, toLanguage);
        }

        private void UpdateButtons()
        {
            bOk.Enabled = Dictionary != null;
        }

        private void OnFirstClick(object sender, EventArgs e)
        {
            MoveToFirst();
        }

        private void MoveToFirst()
        {
            UpdateCurrent();

            _currentItemIdx = 0;

            UpdateView();
        }

        private void OnPreviousClick(object sender, EventArgs e)
        {
            MoveToPrevious();
        }

        private void MoveToPrevious()
        {
            UpdateCurrent();

            _currentItemIdx--;

            UpdateView();
        }

        private void OnNextClick(object sender, EventArgs e)
        {
            MoveToNext();
        }

        private void MoveToNext()
        {
            UpdateCurrent();

            _currentItemIdx++;

            UpdateView();
        }

        private void OnLastClick(object sender, EventArgs e)
        {
            MoveToLast();
        }

        private void MoveToLast()
        {
            UpdateCurrent();

            _currentItemIdx = _entries.Count - 1;

            UpdateView();
        }

        private void OnNewClick(object sender, EventArgs e)
        {
            AddNew();
        }

        private void AddNew()
        {
            UpdateCurrent();

            EditedEntryDescriptor entry = EditedEntryDescriptor.CreateNew();

            _newEntries.Add(entry);
            _entries.Add(entry);

            _currentItemIdx = _entries.Count - 1;

            UpdateView();

            // focus from edit when new entry is added
            // http://code.google.com/p/teach-me/issues/detail?id=43
            tbFrom.Focus();
        }

        private void UpdateTranslationsToolbar()
        {
            tsbTranslate.Enabled = tbFrom.Text.Trim().Length > 0;
            tsbSelectAll.Enabled = boxItems.Items.Count > 0;
            tsbDeselectAll.Enabled = boxItems.Items.Count > 0;
        }

        private void UpdateTranslationsPanel()
        {
            tcbTranslators.Items.Clear();

            bool enabled = true;

            ITranslationProvider[] providers = GetAvailableTranslators();

            if (providers == null || providers.Length == 0)
            {
                enabled = false;
            }

            tsTranslations.Enabled = enabled;
            boxItems.Enabled = enabled;

            if (!enabled)
            {
                return;
            }

            foreach (ITranslationProvider provider in providers)
            {
                tcbTranslators.Items.Add(provider);
            }

            tcbTranslators.SelectedIndex = 0;
        }

        private void OnExampleChanged(object sender, EventArgs e)
        {
            //
        }

        private void OnToChanged(object sender, EventArgs e)
        {
            try
            {
                _ignoreTranslationsCheckStateChanged = true;

                UpdateTranslatedTextsCheckState();
            }
            finally
            {
                _ignoreTranslationsCheckStateChanged = false;
            }
        }

        private void OnFromChanged(object sender, EventArgs e)
        {
            UpdateTranslationsToolbar();
        }

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
                if (keyData == (Keys.Control | Keys.N))
                {
                    if (tsbNew.Enabled)
                    {
                        AddNew();
                    }

                    return true;
                }
                if (keyData == (Keys.Control | Keys.Left))
                {
                    if (tsbPrevious.Enabled)
                    {
                        MoveToPrevious();
                    }

                    return true;
                }
                if (keyData == (Keys.Control | Keys.Right))
                {
                    if (tsbNext.Enabled)
                    {
                        MoveToNext();
                    }

                    return true;
                }
                if (keyData == (Keys.Control | Keys.Home))
                {
                    if (tsbFirst.Enabled)
                    {
                        MoveToFirst();
                    }

                    return true;
                }
                if (keyData == (Keys.Control | Keys.End))
                {
                    if (tsbLast.Enabled)
                    {
                        MoveToLast();
                    }

                    return true;
                }
                if (keyData == (Keys.Control | Keys.T))
                {
                    if (tsbTranslate.Enabled)
                    {
                        Translate();
                    }

                    return true;
                }
            

            return base.ProcessCmdKey(ref msg, keyData);
        }

        public bool NewEntriesAvailable
        {
            get
            {
                return _newEntries.Count > 0;
            }
        }

        public Dictionary Dictionary { get; private set; }

        public List<EditedEntryDescriptor> GetNewEntries()
        {
            return _newEntries;
        }
    }
}
